import tensorflow as tf
"""
在反向传播过程中，神经网络需要对 loss 对应的学习参数求偏导(也叫梯度)，
该值用来乘以学习率然后更新学习参数使用的。
它是通过 tf.gradients 函数来实现的 
"""
weight = tf.Variable([[1, 2]])
y = tf.matmul(weight, [[9], [10]])
"""
第一个参数：求导公式的结果
第二个参数：要求的偏导的参数
"""
grads = tf.gradients(y, weight)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())

    grad_val = sess.run(grads)
    print(grad_val)   # [array([[ 9, 10]], dtype=int32)]


# 对多个公式的多个变量求偏导
tf.reset_default_graph()
weight1 = tf.get_variable('weight1', shape=[2])
weight2 = tf.get_variable('weight2', shape=[2])
weight3 = tf.get_variable('weight3', shape=[2])
weight4 = tf.get_variable('weight4', shape=[2])

y1 = weight1 + weight2 + weight3
y2 = weight3 + weight4

# grad_ys 公式的结果
gradients = tf.gradients([y1, y2], [weight1, weight2, weight3, weight4],
                         grad_ys=[tf.convert_to_tensor([1., 2.]), tf.convert_to_tensor([3., 4.])])

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print(sess.run(gradients))


"""
梯度停止的实现
对于反向传播过程中某种特殊情况需要停止梯度的运算时，
TensorFlow 中提供了 tf.stop_gradients 函数，
被它定义过的节点将没有梯度运算功能
"""

a = weight1 + weight2
a_stopped = tf.stop_gradient(a)
y3 = a_stopped + weight3

gradients1 = tf.gradients(y3, [weight1, weight2, weight3], grad_ys=[tf.convert_to_tensor([1., 2.])])
gradients2 = tf.gradients(y3, [weight3], grad_ys=[tf.convert_to_tensor([1., 2.])])
print(gradients1)  # [None, None, < tf.Tensor 'gradients_1/grad_ys_0:0' shape = (2,) dtype = float32 >]
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    '''
    下面代码会报错
    因为weight1、weight2 的梯度被停止，程序试图去求一个None的梯度，所以报错
    注释掉求 gradients2 就又正确了
    '''
    # print(sess.run(gradients1))
    print(sess.run(gradients2))


